package org.jing.ext.web;

import org.jing.core.lang.data.Carrier;
import org.jing.core.lang.JingException;
import org.jing.core.lang.data.Pair2;
import org.jing.core.util.ClassUtil;
import org.jing.core.util.GenericUtil;
import org.jing.core.util.StringUtil;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.net.InetAddress;

/**
 * Description: <br>
 *
 * @author bks <br>
 * @since 2020-09-04 <br>
 */
public class WebUtil {
    private static final int[][] IP2NAME_OPERATION = {
        {0,1,2,3},{0,1,3,2},{0,2,1,3},{0,2,3,1},{0,3,1,2},{0,3,2,1},
        {1,0,2,3},{1,0,3,2},{1,2,0,3},{1,2,3,0},{1,3,0,2},{1,3,2,0},
        {2,0,1,3},{2,0,3,1},{2,1,0,3},{2,1,3,0},{2,3,0,1},{2,3,1,0},
        {3,0,1,2},{3,0,2,1},{3,1,0,2},{3,1,2,0},{3,2,0,1},{3,2,1,0}
    };

    private static final int[][] IP2NAME_LENGTH = {{}, {}, {9,11,12,15},{6,7,8,9},{5,6,6,8},{4,5,6,7},{4,4,5,6},{3,4,5,5},{3,4,4,5},{3,4,4,5},{3,4,4,5},{3,3,4,5},{3,3,4,4},{3,3,4,4},{3,3,4,4},{3,3,4,4},{3,3,3,4},{2,3,3,4},{2,3,3,4},{2,3,3,4},{2,3,3,4},{2,3,3,4},{2,3,3,4},{2,3,3,4},{2,3,3,4},{2,3,3,4},{2,3,3,4},{2,3,3,3},{2,3,3,3},{2,3,3,3},{2,3,3,3},{2,3,3,3},{2,3,3,3},{2,3,3,3},{2,3,3,3},{2,2,3,3},{2,2,3,3}};

    public static byte[] getRequestPostBytes(HttpServletRequest request) {
        try {
            int contentLength = request.getContentLength();
            if (contentLength < 0) {
                return null;
            }
            byte[] buffer = new byte[contentLength];
            int readLen;
            for (int i$ = 0; i$ < contentLength; ) {
                readLen = request.getInputStream().read(buffer, i$, contentLength - 1);
                if (readLen == -1) {
                    break;
                }
                i$ += readLen;
            }
            return buffer;
        }
        catch (JingException e) {
            throw e;
        }
        catch (Throwable t) {
            throw new JingException(t);
        }
    }

    public static String getRequestPostString(HttpServletRequest request) {
        try {
            byte[] buffer = getRequestPostBytes(request);
            if (null != buffer) {
                String charSet = request.getCharacterEncoding();
                charSet = StringUtil.ifEmpty(charSet, "utf-8");
                return new String(buffer, charSet);
            }
        }
        catch (JingException e) {
            throw e;
        }
        catch (Throwable t) {
            throw new JingException(t);
        }
        return null;
    }

    public static String getRequestIp(HttpServletRequest request) {
        String ipAddress = request.getHeader("x-forwarded-for");
        if (StringUtil.isEmpty(ipAddress) || "unknown".equalsIgnoreCase(ipAddress)) {
            ipAddress = request.getHeader("Proxy-Client-IP");
        }
        if (StringUtil.isEmpty(ipAddress) || "unknown".equalsIgnoreCase(ipAddress)) {
            ipAddress = request.getHeader("WL-Proxy-Client-IP");
        }
        if (StringUtil.isEmpty(ipAddress) || "unknown".equalsIgnoreCase(ipAddress)) {
            ipAddress = request.getRemoteAddr();
            if ("127.0.0.1".equals(ipAddress) || "0:0:0:0:0:0:0:1".equals(ipAddress)) {
                InetAddress inetAddress;
                try {
                    inetAddress = InetAddress.getLocalHost();
                    ipAddress = inetAddress.getHostAddress();
                }
        catch (JingException e) {
            throw e;
        }
        catch (Throwable t) {
                   throw new JingException(t);
                }
            }
        }
        if (StringUtil.isNotEmpty(ipAddress) && ipAddress.length() > 15) {
            if (ipAddress.contains(",")) {
                ipAddress = ipAddress.substring(0, ipAddress.indexOf(","));
            }
        }
        return ipAddress;
    }

    public static String generateNameByIPAddress(String ipAddress, int radix) {
        if (radix > Character.MAX_RADIX || radix < Character.MIN_RADIX) {
            throw new JingException("Invalid Options");
        }
        StringBuilder name = new StringBuilder();
        if (StringUtil.isEmpty(ipAddress)) {
            throw new JingException("Invalid IP Address");
        }
        String[] ipAddressSplit = ipAddress.split("\\.");
        if (ipAddressSplit.length != 4) {
            throw new JingException("Invalid IP Address");
        }
        int[] ipAddressIntSplit = new int[4];
        int sum = 0;
        for (int i$ = 0; i$ < 4; i$ ++) {
            ipAddressIntSplit[i$] = Integer.parseInt(ipAddressSplit[i$]);
            sum += ipAddressIntSplit[i$];
        }
        int splitMethodIndex = sum % IP2NAME_OPERATION.length;
        int[] splitMethod = IP2NAME_OPERATION[splitMethodIndex];
        int[] newIpAddressArr = new int[4];
        int[] anotherIpAddressArr = new int[4];
        int tempIp = 0;
        for (int i$ = 0; i$ < 4; i$ ++) {
            newIpAddressArr[i$] = ipAddressIntSplit[splitMethod[i$]];
            anotherIpAddressArr[i$] = (tempIp + newIpAddressArr[i$] + radix) * (i$ + 1);
            tempIp = anotherIpAddressArr[i$];
            name.append(new StringBuffer(StringUtil.pad(Integer.toString(anotherIpAddressArr[i$], radix), StringUtil.PAD_MODEL_LEFT, '0', IP2NAME_LENGTH[radix][i$])).reverse());
        }
        return name.toString();
    }

    public static String restoreIpAddressByName(String name, int radix) {
        if (radix > Character.MAX_RADIX || radix < Character.MIN_RADIX) {
            throw new JingException("Invalid Options");
        }
        StringBuilder ipAddress = new StringBuilder();
        try {
            int[] ipAddressArr = new int[4];
            int sum = 0;
            int tempIp = 0;
            String p;
            int index = 0;
            int start;
            int end = 0;
            for (int i$ = 0; i$ < 4; i$ ++) {
                start = end;
                end += IP2NAME_LENGTH[radix][i$];
                p = name.substring(start, end);
                p = new StringBuffer(p).reverse().toString();
                ipAddressArr[i$] = Integer.parseInt(p, radix);
                ipAddressArr[i$] /=  (i$ + 1);
                ipAddressArr[i$] -= tempIp + radix;
                tempIp = (ipAddressArr[i$] + tempIp + radix) * (i$ + 1);
                sum += ipAddressArr[i$];
            }
            int splitMethodIndex = sum % IP2NAME_OPERATION.length;
            int[] splitMethod = IP2NAME_OPERATION[splitMethodIndex];
            int times = 0;
            while (times < 4) {
                if (0 != times) {
                    ipAddress.append(".");
                }
                for (int i$ = 0; i$ < 4; i$ ++) {
                    if (splitMethod[i$] == times) {
                        ipAddress.append(ipAddressArr[i$]);
                    }
                }
                times ++;
            }
        }
        catch (JingException e) {
            throw e;
        }
        catch (Throwable t) {
            throw new JingException(t);
        }
        return ipAddress.toString();
    }

    public static String generateNameByIPAddress(String ipAddress) {
        return generateNameByIPAddress(ipAddress, Character.MAX_RADIX);
    }

    public static String restoreIpAddressByName(String name) {
        return restoreIpAddressByName(name, Character.MAX_RADIX);
    }

    public static Carrier getCookiesCarrier(HttpServletRequest request) {
        Carrier cookiesC = new Carrier();
        Cookie[] cookies = request.getCookies();
        int size4Cookies = GenericUtil.count(cookies);
        for (int i$ = 0; i$ < size4Cookies; i$++) {
            cookiesC.setValue(cookies[i$].getName(), cookies[i$].getValue());
        }
        return cookiesC;
    }

    public static void addCookies2Response(Carrier cookiesC, HttpServletResponse response) {
        Cookie cookie;
        for (Pair2<String, Object> pair : cookiesC.getValueChildList()) {
            cookie = new Cookie(pair.getA(), ClassUtil.cast2String(pair.getB()));
            cookie.setPath("/");
            response.addCookie(cookie);
        }
    }

    public static boolean isUrl(String url) {
        return url.startsWith("http://") || url.startsWith("https://");
    }

    public static void addCookie(HttpServletResponse response, String key, String value) {
        Cookie cookie = createCookie(key, value);
        response.addCookie(cookie);
    }

    public static void clearCookie(HttpServletResponse response, String key) {
        Cookie cookie = createCookie(key, "", 0);
        response.addCookie(cookie);
    }

    public static String concatUrl(String... paths) {
        StringBuilder sb = new StringBuilder();
        for (int i$ = 0, l$ = GenericUtil.count(paths); i$ < l$; ++ i$) {
            if (sb.length() == 0) {
                sb.append(paths[i$]);
            } else if (sb.charAt(sb.length() - 1) == '/' && paths[i$].startsWith("/")) {
                sb.append(paths[i$].substring(1));
            } else if (sb.charAt(sb.length() - 1) != '/' && !paths[i$].startsWith("/")) {
                sb.append('/').append(paths[i$]);
            } else {
                sb.append(paths[i$]);
            }
        }
        return sb.toString();
    }

    public static void output2ServletResponse(HttpServletResponse response, int httpStatus, String content) {
        try (ServletOutputStream sos = response.getOutputStream()) {
            response.setStatus(httpStatus);
            sos.write(content.getBytes());
            sos.flush();
        }
        catch (Throwable t) {
            throw new JingException(t);
        }
    }

    public static Cookie createCookie(String name, String value) {
        Cookie cookie = new Cookie(name, value);
        cookie.setPath("/");
        return cookie;
    }

    public static Cookie createCookie(String name, String value, int maxAge) {
        Cookie cookie = new Cookie(name, value);
        cookie.setMaxAge(maxAge);
        cookie.setPath("/");
        return cookie;
    }
}
